package netmodule

import (
	"encoding/binary"
	"go_server/common"
	"go_server/components"
	"net"
	"sync"

	"github.com/astaxie/beego/logs"
)

type OutMsgQueueGroup struct {
	OutMsgQueueChanMap map[uint16]chan *common.MsgQueueItem
	OutWaitGroup       sync.WaitGroup
}

var OutMsgQueueGroupInstance *OutMsgQueueGroup = nil

func NewOutMsgQueue() {
	OutMsgQueueGroupInstance = new(OutMsgQueueGroup)
	OutMsgQueueGroupInstance.OutMsgQueueChanMap = make(map[uint16]chan *common.MsgQueueItem)

	//创建指定个数的chan
	for i := 0; i < components.OutputNum; i++ {
		OutMsgQueueGroupInstance.OutMsgQueueChanMap[uint16(i)] = make(chan *common.MsgQueueItem, components.OutputSize)
		//开启协程去处理
		OutMsgQueueGroupInstance.OutWaitGroup.Add(1)
		go DeliverOutMsg(uint16(i))
	}
	logs.Debug("[OutMsgQueue.go:30]成功创建", components.OutputNum, "个用户发送数据的协程", " 每个最大缓冲区为：", components.MaxMsgQueueSize)

}

func PostOutMsg(id uint16, body []byte, bodylen uint32, session *common.SessionItem) {
	NewMsgQueueItem := new(common.MsgQueueItem)
	NewMsgQueueItem.ConSessionItem = session

	NewPackage := new(common.PackageStruct)
	NewPackage.Head = new(common.PackageHead)
	NewPackage.Head.PkgId = id
	NewPackage.Head.PkgBodyLen = bodylen
	NewPackage.Body = body

	NewMsgQueueItem.TruePackage = NewPackage

	//hash到指定的map中的某个与发送协程绑定的chan中，保证回包的正确性避免错乱
	OutMsgQueueGroupInstance.OutMsgQueueChanMap[uint16(session.ForHashId)%uint16(components.OutputNum)] <- NewMsgQueueItem

}

func DeliverOutMsg(index uint16) {
	defer OutMsgQueueGroupInstance.OutWaitGroup.Done()

	for {
		select {
		case <-TcpServer.AcceptClose:
			return
		case NeededOutMsg, ok := <-OutMsgQueueGroupInstance.OutMsgQueueChanMap[index]:
			if !ok {
				return
			}
			err := WritePackage(NeededOutMsg)
			if err != nil {
				logs.Error("Write Error:", err.Error())
			}
		}
	}

}

// 执行
func WritePackage(NeededOutMsg *common.MsgQueueItem) error {

	if NeededOutMsg == nil {
		return common.ErrOutMsgEmpty
	}
	headBuffer := make([]byte, 6)
	binary.BigEndian.PutUint16(headBuffer[0:2], NeededOutMsg.TruePackage.Head.PkgId)
	binary.BigEndian.PutUint32(headBuffer[2:6], NeededOutMsg.TruePackage.Head.PkgBodyLen)

	buffers := net.Buffers{headBuffer, NeededOutMsg.TruePackage.Body} //避免创建新的切片

	//能保证切片已经发送到缓冲区了
	if _, err := buffers.WriteTo(NeededOutMsg.ConSessionItem.Conn); err != nil {
		// 处理错误
		return err
	}
	return nil

	// fullLen := NeededOutMsg.TruePackage.Head.PkgBodyLen + 6 //这个4表示的是包头的长度
	// buff := make([]byte, fullLen)
	// binary.BigEndian.PutUint16(buff[0:2], NeededOutMsg.TruePackage.Head.PkgId)
	// binary.BigEndian.PutUint32(buff[2:6], NeededOutMsg.TruePackage.Head.PkgBodyLen)
	// copySize := copy(buff[6:], NeededOutMsg.TruePackage.Body)
	// if copySize != int(NeededOutMsg.TruePackage.Head.PkgBodyLen) {
	// 	return common.ErrMsgCopy
	// }
	// total := 0
	// for {
	// 	tmpSize, err := NeededOutMsg.ConSessionItem.Conn.Write(buff[total:])
	// 	total += tmpSize
	// 	if err == nil && total >= int(fullLen) {
	// 		break
	// 	} else if err != nil {
	// 		return err
	// 	}
	// }
	// return nil
}
